package main

import (
	"context"
	//"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	//"github.com/op/go-logging"
	"github.com/valyala/fasthttp"
)

type RouteResponse struct {
	resp     *http.Response //得到的响应
	addr     string
	isForced bool
}

type Router struct {
	client  *http.Client
	timeout int
	gateway GateWay
	counter *counter
}

func (r *Router) init() error {
	r.client = &http.Client{
		Timeout: time.Duration(r.timeout) * time.Second,
	}
	return nil
}

//handle调用了routedCall，
func (r *Router) handle(incomeCtx *fasthttp.RequestCtx) Status {
	var isFallback, isNull, isForced bool
	var code int

	stdAddrs, fbkAddrs := r.gateway.GetAddresses(incomeCtx) //从过来的请求中得到地址
	result := r.routedCall(incomeCtx.ID(), stdAddrs)        //正确的地址了

	if (result == nil || result.isForced) && len(fbkAddrs) > 0 { //如果广播超时还找不到就去找mixfs
		isFallback = true
		LOG.Info("---Use Mixfs---")
		result = r.routedCall(incomeCtx.ID(), fbkAddrs) //才调用mixfs
	} else {
		if len(stdAddrs) == 1 {
			LOG.Info("---Use Redis---")
		} else {
			LOG.Info("---Use Broadcast---")
		}
	}

	if result == nil { //这是都找不到的情况下
		isNull = true
		code = http.StatusNotFound
		incomeCtx.Response.SetStatusCode(http.StatusNotFound)
		return Status{
			IsForced:   isForced,   //如果有三个的话，最后一个一定要强制输出
			IsFallback: isFallback, //是不是后备地址
			IsNull:     isNull,     //地址是不是空
			Code:       code,       //返回码404这些
		}
	}

	isForced = result.isForced
	code = result.resp.StatusCode
	body, err := ioutil.ReadAll(result.resp.Body) //

	defer result.resp.Body.Close() //程序在使用完回复后必须关闭回复的主体

	if err == nil {
		incomeCtx.Response.SetStatusCode(result.resp.StatusCode)
		incomeCtx.Response.SetBody(body)
		l := result.resp.Header.Get("Content-Length")
		ll, _ := strconv.Atoi(l) //string到int的转换
		incomeCtx.Response.Header.SetContentLength(ll)

		for k, v := range result.resp.Header {
			incomeCtx.Response.Header.Set(k, strings.Join(v, ",")) //冒号连接v
		}
	}
	//LOG.Infof (string(body))
	return Status{
		IsForced:   isForced, //什么样的状态
		IsFallback: isFallback,
		IsNull:     isNull, //找不到
		Address:    result.addr,
		Code:       code,
	}
}

func (r *Router) Handler(incomeCtx *fasthttp.RequestCtx) {
	r.counter.Call(incomeCtx, r.handle) //这里再使用r.handle,再给Call用
}

func (r *Router) routedCall(originID uint64, requests []*http.Request) (result *RouteResponse) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(r.timeout))

	for _, v := range requests {
		LOG.Info(v.URL.String())
	}

	select {
	case result = <-r.routedCallAll(originID, requests, cancel): //每条case语句都是一个IO操作，如果result读到数据
		if result.resp.ContentLength == 0 { //找不到数据
			LOG.Errorf("[%d] %s head content-length is 0\n", originID, result.addr)
		} //长度是0
		cancel()

	case <-ctx.Done(): //当一个工作需要取消时，使用Done()返回一个管道。可能会返回nil。多次使用Done返回的数据一样。
		LOG.Noticef("[%d] DEFAULT resp 404\n", originID) //绿色显示
	}
	return
}

func (r *Router) routedCallAll(originID uint64, reqs []*http.Request, cancel context.CancelFunc) <-chan *RouteResponse {
	ch := make(chan *RouteResponse) //创建无缓冲的管道,类型是RouteResponse
	if len(reqs) == 0 {             //没有请求
		cancel() //强制停止
		return ch
	}

	cd := int32(len(reqs)) //转为int32位,有多少个请求就是多少个cd

	for _, req := range reqs {
		go r.routedCallOne(originID, req, &cd, ch) //并发调用routedCallOne,有reqs个，就调用多少个goroutine.所以是一批数据来
	}
	return ch
}

func (r *Router) routedCallOne(originID uint64, req *http.Request, cd *int32, output chan *RouteResponse) {
	resp, err := r.counter.call(req, r.client.Do) //Do中，非200状态码不会引发错误，就是下面的可能不是200
	if err != nil {
		LOG.Errorf("[%d] err while get %s , err %v", originID, req.Host, err)
		return
	}
	code := resp.StatusCode
	if code == http.StatusOK || code == http.StatusPartialContent { //是200或者206 {
		result := &RouteResponse{
			addr: req.Host,
			resp: resp,
		}
		output <- result
		return //返回就不会判断isforced
	} else {
		LOG.Noticef("[%d] req from %s return code is %d\n", originID, req.Host, code)
	}

	//atomic.AddInt32是cd-1,这样，如果等于0了，把result放到output管道中
	if atomic.AddInt32(cd, -1) == 0 {
		result := &RouteResponse{
			addr:     req.Host,
			resp:     resp,
			isForced: true, //最后一个了，要退出
		}
		output <- result
		return
	}
	return
}
